package SceneTrigger

import (
	"PBMessage"
	//"SGNet"
	. "GameLogic/Scene"
	//"fmt"
	proto "github.com/golang/protobuf/proto"
	"misc/Clojure"
	. "misc/Clojure/parse"
)

type Trigger struct {
	Events        []IEvent
	ConditionNode Node
	Actions       []ITriggerAction
	Scene         *Scene
	Desc          string
	ID            int

	RunningInstances []*TriggerInstance

	Context *Clojure.Context
}

type TriggerInstance struct {
	trigger      *Trigger
	IsRunning    bool
	CurActioni   int
	IsWaitAction bool
	Context      *Clojure.Context

	Actions []ITriggerAction

	triggerUnit IUnit
}

func NewTriggerInstance(trigger *Trigger) *TriggerInstance {
	newInstance := &TriggerInstance{}
	newInstance.trigger = trigger
	newInstance.Context = Clojure.NewContext(trigger.Context)
	//Clone Actions
	newInstance.Actions = make([]ITriggerAction, len(trigger.Actions))
	for i := 0; i < len(trigger.Actions); i++ {
		if trigger.Actions[i].GetType() == TriggerAction_NonBlock {
			newInstance.Actions[i] = trigger.Actions[i]
		} else {
			args := trigger.Actions[i].GetActionInfo().ActionNode
			actionName := args.Children()[0].(*SymbolNode).Val
			fn := GetBlockActionNewFunc(actionName)
			blockAction := fn(args.Children())
			blockAction.GetActionInfo().ActionNode = args
			newInstance.Actions[i] = blockAction
		}
	}
	return newInstance
}

func (this *TriggerInstance) Run(triggerUnit IUnit) {
	this.Context.Define("TriggerUnit", &ObjectNode{Val: triggerUnit})

	this.IsRunning = true
	this.CurActioni = 0
	this.IsWaitAction = false
	this.triggerUnit = triggerUnit
}

func (this *TriggerInstance) Update() {
	if this.CurActioni >= len(this.Actions) {
		this.IsRunning = false
	}
	if this.IsRunning == false {
		return
	}
	action := this.Actions[this.CurActioni]
	for this.CurActioni < len(this.Actions) && !this.IsWaitAction {
		if action.GetType() == TriggerAction_Block {
			this.IsWaitAction = true
			action.OnExec(this.Context)
		} else {
			this.IsWaitAction = false
			action.OnExec(this.Context)
			this.CurActioni++
			if this.CurActioni == len(this.Actions) {
				this.IsRunning = false
				return
			}
			action = this.Actions[this.CurActioni]
		}
	}
	if this.IsWaitAction && action != nil {
		blockAction := action.(IBlockTrigerAction)
		blockAction.Update()
		if blockAction.IsFinish() {
			this.IsWaitAction = false
			this.CurActioni++
			blockAction.OnExit()
		}
	}
}

func (this *Trigger) OnTrigger(triggerUnit IUnit) {

	this.Context.Define("TriggerUnit", &ObjectNode{Val: triggerUnit})

	if this.IsEvent() && this.IsCondition() {
		exec := &PBMessage.SC_ExecTrigger{}
		exec.TriggerId = proto.Int32(int32(this.ID))
		this.Scene.SendToAllPlayer(PBMessage.PBID_SC_ExecTrigger, exec)
		this.RunActions(triggerUnit)
	}
}

func (this *Trigger) IsEvent() bool {
	for ei := 0; ei < len(this.Events); ei++ {
		if this.Events[ei].OnEventCondition() {
			return true
		}
	}
	return false
}
func (this *Trigger) IsCondition() bool {
	if this.ConditionNode != nil {
		return Clojure.Evaluate(this.ConditionNode, this.Context).(*BoolNode).Val
	}
	return true
}

func (this *Trigger) RunActions(triggerUnit IUnit) {
	this.GetRunInstance().Run(triggerUnit)
}
func (this *Trigger) GetRunInstance() *TriggerInstance {
	var freeInstance *TriggerInstance = nil
	for i := 0; i < len(this.RunningInstances); i++ {
		if this.RunningInstances[i].IsRunning == false {
			freeInstance = this.RunningInstances[i]
		}
	}
	if freeInstance != nil {
		return freeInstance
	}
	newIsntance := NewTriggerInstance(this)
	if this.RunningInstances == nil {
		this.RunningInstances = make([]*TriggerInstance, 0)
	}
	this.RunningInstances = append(this.RunningInstances, newIsntance)
	return newIsntance
}

func (this *Trigger) AddEvent(event IEvent) {
	this.Events = append(this.Events, event)
}

func (this *Trigger) Update() {
	for i := 0; i < len(this.RunningInstances); i++ {
		this.RunningInstances[i].Update()
	}
}
